package com.zhao.dota.config;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Maps;
import com.zhao.dota.mapper.SpringScheduledCronMapper;
import com.zhao.dota.model.SpringScheduledCron;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.CronTask;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronSequenceGenerator;
import org.springframework.scheduling.support.PeriodicTrigger;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.*;

@Configuration
@EnableScheduling   // 开启定时任务
@Slf4j
public class ScheduledConfig implements SchedulingConfigurer {

    private final ConcurrentHashMap<Integer, ScheduledFuture<?>> scheduledFutures = new ConcurrentHashMap<>();
    private final ConcurrentHashMap<Integer, CronTask> cronTasks = new ConcurrentHashMap<>();
    @Resource
    private ApplicationContext context;
    @Resource
    private SpringScheduledCronMapper springScheduledCronMapper;
    private volatile ScheduledTaskRegistrar registrar;
    private Map<Integer, SpringScheduledCron> taskConstants = Maps.newConcurrentMap();

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        this.registrar = taskRegistrar;
        SpringScheduledCron params = new SpringScheduledCron();
        params.setStatus(true);
        List<SpringScheduledCron> crons = springScheduledCronMapper.selectList(Wrappers.query(params));
        for (SpringScheduledCron cron : crons) {
            taskConstants.put(cron.getId(), cron);
        }
        taskRegistrar.addTriggerTask(() -> {
            if (!CollectionUtils.isEmpty(taskConstants)) {
                if (log.isTraceEnabled()) {
                    log.trace("检测动态定时任务列表...");
                }
                this.refreshTasks(Maps.newHashMap(taskConstants));
            }
        }, triggerContext -> new PeriodicTrigger(5L, TimeUnit.SECONDS).nextExecutionTime(triggerContext));

    }

    private void refreshTasks(Map<Integer, SpringScheduledCron> tasks) {
        //取消已经删除的策略任务
        Set<Integer> taskIds = scheduledFutures.keySet();
        for (Integer taskId : taskIds) {
            if (!exists(tasks, taskId)) {
                scheduledFutures.get(taskId).cancel(false);
            }
        }
        for (SpringScheduledCron task : tasks.values()) {
            String expression = task.getCronExpression();
            if (!StringUtils.hasText(expression) || !CronSequenceGenerator.isValidExpression(expression)) {
                log.error("定时任务DynamicTask cron表达式不合法: " + expression);
                continue;
            }
            //如果配置一致，则不需要重新创建定时任务
            if (scheduledFutures.containsKey(task.getId())
                    && cronTasks.get(task.getId()).getExpression().equals(expression)) {
                continue;
            }
            //如果策略执行时间发生了变化，则取消当前策略的任务
            if (scheduledFutures.containsKey(task.getId())) {
                scheduledFutures.remove(task.getId()).cancel(false);
                cronTasks.remove(task.getId());
            }

            Class<?> clazz;
            Object o;
            try {
                clazz = Class.forName(task.getCronKey());
                o = context.getBean(clazz);
            } catch (ClassNotFoundException e) {
                throw new IllegalArgumentException("spring_scheduled_cron表数据" + task.getCronKey() + "有误", e);
            } catch (BeansException e) {
                throw new IllegalArgumentException(task.getCronKey() + "未纳入到spring管理", e);
            }
            CronTask cronTask = new CronTask((Runnable) o, expression);
            ScheduledFuture<?> future = registrar.getScheduler().schedule(cronTask.getRunnable(), cronTask.getTrigger());
            cronTasks.put(task.getId(), cronTask);
            scheduledFutures.put(task.getId(), future);
        }
    }

    public void addOrUpdateTask(SpringScheduledCron cron) {
        taskConstants.put(cron.getId(), cron);
    }

    public boolean disableTask(Integer cronId) {
        SpringScheduledCron remove = taskConstants.remove(cronId);
        return remove != null;
    }

    private boolean exists(Map<Integer, SpringScheduledCron> tasks, Integer taskId) {
        return tasks.containsKey(taskId);
    }

    @Bean
    public Executor taskExecutor() {
        return Executors.newScheduledThreadPool(10);
    }

}
